package com.black.utils.commons.pool;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author ylx
 */
@Slf4j
@Getter
public class Pool<T> {
    protected final int MAX_RETRY_ATTEMPTS = 5;
    protected final int MAX_SLEEP_TIME = 8000;
    protected final GenericObjectPool<T> genericObjectPool;
    protected final PoolFactory<T> factory;
    protected final AtomicBoolean isRefreshing = new AtomicBoolean(false);

    public Pool(PoolFactory<T> factory, int minIdle) {
        this.factory = factory;
        this.genericObjectPool = new GenericObjectPool<>(factory);
        // 维持池中最小活跃的个数 为0时归还即销毁,借出即新建 无意义池
        this.genericObjectPool.setMinIdle(minIdle);
        this.genericObjectPool.setTestOnBorrow(true);
        this.genericObjectPool.setTestOnReturn(true);
    }

    /**
     * 从池中借用一个 Object 实例。
     * 这个方法会尝试多次从池中获取 Object，如果在尝试过程中遇到池正在刷新的情况，则会等待一秒后再次尝试，
     * 直到尝试次数达到最大值或成功获取到 Object。
     *
     * @return Object 如果成功从池中借用到 Object 实例，则返回该实例；否则返回null。
     */
    public T borrowObject() {
        T object = null;
        try {
            for (int attempt = 0; attempt < MAX_RETRY_ATTEMPTS; attempt++) {
                log.debug("attempt to borrow [{}] times", attempt);
                if (!isRefreshing.get()) {
                    object = genericObjectPool.borrowObject();
                    String simpleName = object.getClass().getSimpleName();
                    log.debug("borrow object {} [{}] success", simpleName, object);
                    return object;
                }
                // 引入指数退避机制
                Thread.sleep(Math.min(1000 * (1 << attempt), MAX_SLEEP_TIME));
            }
        } catch (InterruptedException e) {
            log.error("borrowObject() interrupted");
        } catch (Exception e) {
            log.error("borrowObject() error", e);
        }
        return object;
    }

    /**
     * 将 Object 对象返回到通用对象池中。
     * 这个方法主要用于在使用完 Object 后，将其归还到对象池，以便于后续再次使用，降低对象创建的开销。
     *
     * @param object 需要被返回的 Object 对象。该对象必须是在之前从对象池中获取的。
     */
    public void returnObject(T object) {
        if (null != object) {
            genericObjectPool.returnObject(object);
            log.debug("return {} {}", object.getClass().getSimpleName(), object);
        }
    }

    /**
     * 更新配置方法
     * 此方法首先尝试设置刷新标志为true，如果设置成功，则表示当前没有其他线程正在执行刷新操作，可以继续执行；
     * 如果设置失败，则表示已有线程正在执行刷新，此时当前线程将直接返回，不执行刷新操作。
     * 方法主要完成两个主要步骤：
     * 1. 等待所有从池中借出的对象被归还。
     * 2. 清空对象池, 然后重新由工厂根据新配置生成对象。
     */
    public void updateConfig() {
        factory.updateConfig();
        if (!isRefreshing.compareAndSet(false, true)) {
            // 如果已经在刷新过程中，则不重复执行
            return;
        }

        CountDownLatch latch = new CountDownLatch(1);
        Thread clearThread = clearThread(latch);

        try {
            // 等待safetyThread完成
            latch.await();
        } catch (InterruptedException e) {
            log.error("Timeout waiting for config update", e);
            // 尝试中断清理线程（如果还在执行）
            clearThread.interrupt();
        } finally {
            // 确保刷新标志被重置
            isRefreshing.set(false);
        }
    }

    private Thread clearThread(CountDownLatch latch) {
        Thread clearThread = new Thread(() ->
        {
            try {
                // 等待所有对象归还
                while (genericObjectPool.getNumActive() > 0) {
                    // 使用CountDownLatch实现带超时的等待
                    latch.await(1, TimeUnit.SECONDS);
                }
                genericObjectPool.clear();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("updateConfig() interrupted", e);
            } finally {
                latch.countDown();
            }
        });
        clearThread.start();
        return clearThread;
    }

    public void close() {
        genericObjectPool.clear();
        genericObjectPool.close();
    }
}
